package com.jdrx.ocp.service;

import com.jdrx.ocp.beans.commons.SysConstant;
import com.jdrx.ocp.beans.constans.*;
import com.jdrx.ocp.beans.dto.resource.CreateResourceDTO;
import com.jdrx.ocp.beans.dto.resource.QueryResourceDTO;
import com.jdrx.ocp.beans.dto.resource.QueryResourceTreeDTO;
import com.jdrx.ocp.beans.dto.resource.UpdateResourceDTO;
import com.jdrx.ocp.beans.entity.ResourcePO;
import com.jdrx.ocp.beans.entity.UserPO;
import com.jdrx.ocp.beans.entity.util.ExceptionMessageFilter;
import com.jdrx.ocp.beans.event.NoticesEvent;
import com.jdrx.ocp.beans.vo.QueryResourceTreeVO;
import com.jdrx.ocp.beans.vo.QueryResourceVO;
import com.jdrx.ocp.beans.vo.QueryUserResourceVO;
import com.jdrx.ocp.dao.ResourceDAO;
import com.jdrx.ocp.dao.RoleResourceDAO;
import com.jdrx.platform.commons.rest.beans.dto.IdDTO;
import com.jdrx.platform.commons.rest.exception.BizException;
import com.jdrx.sw.sewage.service.common.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 资源管理
 *
 * @Author: Huangxg
 * @Date: 2018/11/20 15:57
 */
@Service
public class ResourceService {

    private static final Logger logger = LoggerFactory.getLogger(ResourceService.class);
    /**
     * 资源DAO
     */
    @Autowired
    private ResourceDAO resourceDAO;

    @Autowired
    private RoleResourceDAO roleResourceDAO;

    @Autowired
    private UserService userService;

    @Autowired
    RedisService redisService;

    @Autowired
    private SettingService settingService;

    @Autowired
    private NoticeEventService noticeEventService;

    /**
     * 搜索条件处理
     *
     * @param queryResourceDTO
     * @param query
     * @return
     */
    private Map<String, Object> initQueryParams(QueryResourceDTO queryResourceDTO, Map<String, Object> query) throws BizException {
        query.put("name", queryResourceDTO.getName());
        query.put("prefix", queryResourceDTO.getPrefix());
        query.put("type", queryResourceDTO.getType());
        query.put("url", queryResourceDTO.getUrl());
        return query;
    }

    /**
     * 资源信息查询
     *
     * @param queryResourceDTO
     * @return
     */
    public List<QueryResourceVO> queryResource(QueryResourceDTO queryResourceDTO) throws BizException {
        try {
            Map<String, Object> query = new HashMap<String, Object>(9);
            initQueryParams(queryResourceDTO, query);
            return generateQueryResourceVO(resourceDAO.listBy(query));
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException("获取数据失败！");
        }
    }

    /**
     * 获取资源树
     *
     * @return
     * @throws BizException
     */
    public List<QueryResourceTreeVO> getResourceForTree(Integer userId, QueryResourceTreeDTO queryResourceTreeDTO) throws BizException {
        try {
            Map<String, Object> queryMap = new HashMap<>(2);
            queryMap.put("delFlag", EDeleteFlag.NORMAL.getKey());
            queryMap.put("prefix", queryResourceTreeDTO.getPrefix());
            if (userId != null) {
                UserPO userPO = userService.getUserById(userId);
                if (userPO == null) {
                    throw new BizException("用户信息异常！");
                } else if (!SysConstant.SYSTEM_USER_NAME.equals(userPO.getName()) &&
                        !SysConstant.ADMIN_USER_NAME.equals(userPO.getName()) &&
                        !EYesOrNo.YES.getKey().equals(userPO.getIsAdmin()) &&
                        !EYesOrNo.YES.getKey().equals(userPO.getIsSystem())) {
                    //queryMap.put("userId", userId);
                }
            }
            List<ResourcePO> list = resourceDAO.listByUser(queryMap);
            return getResourceTree(list, 0);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException("获取数据失败：" + e.getMessage());
        }
    }

    /**
     * 查询返回结构体
     *
     * @param datas
     * @return
     * @throws BizException
     */
    private List<QueryResourceVO> generateQueryResourceVO(List<ResourcePO> datas) throws BizException {
        try {
            List<QueryResourceVO> voList = new ArrayList<QueryResourceVO>();
            if (CollectionUtils.isEmpty(datas)) {
                return voList;
            }
            QueryResourceVO vo = null;
            for (ResourcePO resPO : datas) {
                vo = new QueryResourceVO();
                BeanUtils.copyProperties(resPO, vo);
                voList.add(vo);
            }
            return voList;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException("获取数据失败！");
        }
    }

    /**
     * 创建资源数据
     *
     * @param createResourceDTO
     * @return
     * @throws BizException
     */
    @Transactional(rollbackFor = Exception.class)
    public int createResource(CreateResourceDTO createResourceDTO, Integer uId, String tid) throws BizException {
        try {
            Map<String, Object> queryMap = new HashMap<String, Object>(4);
            queryMap.put("exactName", createResourceDTO.getName());
            queryMap.put("pid", createResourceDTO.getPid());
            List<ResourcePO> oldResPo = resourceDAO.listBy(queryMap);
            if (!CollectionUtils.isEmpty(oldResPo) && oldResPo.size() > 0) {
                throw new BizException("该资源名已存在");
            }
            Date nowTime = new Date();
            ResourcePO resourcePO = new ResourcePO();
            BeanUtils.copyProperties(createResourceDTO, resourcePO);
            resourcePO.setCreateBy(uId);
            resourcePO.setCreateAt(nowTime);
            resourcePO.setUpdateAt(nowTime);
            resourcePO.setUpdateBy(uId);
            resourcePO.setDelFlag(EDeleteFlag.NORMAL.getKey().toString());
            int num = resourceDAO.insert(resourcePO);
            if (num > 0) {
                this.storageRedisResource(tid);
                noticeEventService.publishEvent(new NoticesEvent(this, EventEnum.RESOURCE, EventActionEnum.CREATE));
            }
            return num;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException(ExceptionMessageFilter.filter(e.getMessage()));
        }
    }

    /**
     * 更新资源数据
     *
     * @param updateResourceDTO
     * @return
     * @throws BizException
     */
    @Transactional(rollbackFor = Exception.class)
    public int updateResource(UpdateResourceDTO updateResourceDTO, Integer uId, String tid) throws BizException {
        try {
            ResourcePO resPO = resourceDAO.getById(updateResourceDTO.getId());
            if (resPO == null) {
                throw new BizException("修改对象不存在或已被删除");
            }
            ResourcePO newResPo = new ResourcePO();
            BeanUtils.copyProperties(updateResourceDTO, newResPo);
            newResPo.setUpdateAt(new Date());
            newResPo.setUpdateBy(uId);
            int num = resourceDAO.update(newResPo);
            if (num > 0) {
                this.storageRedisResource(tid);
                noticeEventService.publishEvent(new NoticesEvent(this, EventEnum.RESOURCE, EventActionEnum.MODIDY));
            }
            return num;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException("更新数据失败！");
        }
    }

    /**
     * 删除资源（标记删除，非物理删除）
     *
     * @param idDTO
     * @return
     * @throws BizException
     */
    @Transactional(rollbackFor = Exception.class)
    public int deleteResource(IdDTO idDTO, Integer uId) throws BizException {
        try {
            Long id = Long.valueOf(idDTO.getId().toString());
            ResourcePO resPO = resourceDAO.getById(id);
            if (resPO == null) {
                throw new BizException("修改对象不存在或已被删除");
            }
            ResourcePO delRes = new ResourcePO();
            delRes.setId(id);
            delRes.setDelFlag(EDeleteFlag.DELETE.getKey());
            delRes.setUpdateAt(new Date());
            int num = resourceDAO.update(delRes);
            if (num > 0) {
                noticeEventService.publishEvent(new NoticesEvent(this, EventEnum.RESOURCE, EventActionEnum.DELETE));
                return roleResourceDAO.deleteByResourceId(Integer.valueOf(idDTO.getId().toString()));
            }
            throw new BizException("执行资源删除失败");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException("删除数据失败！");
        }
    }


    /**
     * List转树
     *
     * @param list
     * @param pid
     * @return
     */
    private List<QueryResourceTreeVO> getResourceTree(List<ResourcePO> list, Object pid) {
        List<QueryResourceTreeVO> itemList = new ArrayList<>();
        QueryResourceTreeVO queryResourceTreeVO = null;
        for (ResourcePO item : list) {
            if (pid.toString().equals(item.getPid().toString())) {
                queryResourceTreeVO = new QueryResourceTreeVO();
                BeanUtils.copyProperties(item, queryResourceTreeVO);
                List<QueryResourceTreeVO> obj = getResourceTree(list, item.getId());
                queryResourceTreeVO.setChildren(obj);
                itemList.add(queryResourceTreeVO);
            }
        }
        return itemList;
    }

    /**
     * 上传资源文件
     * @param file
     * @param userId
     * @throws BizException
     */
	/*@Transactional(rollbackFor = Exception.class)
	public boolean importResource(MultipartFile file , Integer userId) throws BizException {
	    List<ResourcePO> list = getImportResource(file,userId);
	    if(list == null || list.size() == 0){
	        return false;
	    }
	    //节点深度对应实际的ID值
	    Map<String,Long> deepMap = new HashMap<>(7);
	    for(ResourcePO res : list){
	        int deepIndex = res.getPid();
	        //当节点深度为0时表示第一级
	        if(deepIndex == 0){
	            int id = resourceDAO.insert(res);
	            if(id <= 0){
	                throw new BizException("写入资源数据错误");
	            }
	            //注意此处的res.getPid为节点深度
	            deepMap.put(String.valueOf(deepIndex),res.getId());
	        }else{
	            Long pid = 0L ;
	            if(deepMap.containsKey(String.valueOf(deepIndex - 1))){
	                pid = deepMap.get(String.valueOf(deepIndex - 1));
	            }else{
	                throw new BizException("资源树结构断裂异常，深度:"+deepIndex);
	            }
	            res.setPid(pid.intValue());
	            int id = resourceDAO.insert(res);
	            if(id <= 0){
	                throw new BizException("写入资源数据错误");
	            }
	            //记录节点深度对应的id
	            deepMap.put(String.valueOf(deepIndex),res.getId());
	        }
	    }
	    return true;
	}*/

    /**
     * 解析上传文件
     * @param file
     * @param userId
     * @return
     * @throws BizException
     */
	/*private List<ResourcePO> getImportResource(MultipartFile file , Integer userId) throws BizException {
	    Workbook wb = null;
	    List<ResourcePO> resourcePOList = new ArrayList();
	    ResourcePO resourcePO = null;
	    try {
	        wb = new HSSFWorkbook(new POIFSFileSystem(file.getInputStream()));
	    } catch (IOException e) {
	        e.printStackTrace();
	        logger.error(e.getMessage(), e);
	        new BizException("解析数据失败！");
	    }
	    Sheet sheet = wb.getSheetAt(0);
	    Date now = new Date();
	    //跳过第一行
	    for (int i = 1; i < sheet.getLastRowNum(); i++) {
	        Row row = sheet.getRow(i);
	        resourcePO = new ResourcePO();
	        int deep = 0;
	        //最大层级为5
	        for(int k = 0 ; k < 5 ; k++){
	            //当单元格值为空则时跳过
	            if(row.getCell(k) == null){
	                continue;
	            }
	            String name = row.getCell(k).getStringCellValue();
	            if(StringUtils.isNotBlank(name)){
	                resourcePO.setName(name);
	                //注意此处将原本的pid当作节点深度处理在实际写入时需要转换成上级节点的值
	                resourcePO.setPid(k);
	                deep = k;
	                break;
	            }
	        }
	        resourcePO.setUrl(row.getCell(5).getStringCellValue());
	        resourcePO.setPrefix(row.getCell(6).getStringCellValue());
	        if(StringUtils.isBlank(resourcePO.getName()) ||
	                StringUtils.isBlank(resourcePO.getPrefix()) ||
	                StringUtils.isBlank(resourcePO.getUrl())){
	            throw new BizException(String.format("模板数据错误在%s行%s列，行数据中名称、地址、平台标识符是必须的",i,deep));
	        }
	        resourcePO.setMenuIcon(row.getCell(7).getStringCellValue());
	        resourcePO.setNameEn(row.getCell(8).getStringCellValue());
	        resourcePO.setServerType((int) Math.ceil(row.getCell(9).getNumericCellValue()));
	        resourcePO.setSort((int) Math.ceil(row.getCell(10).getNumericCellValue()));
	        resourcePO.setCreateAt(now);
	        resourcePO.setUpdateAt(now);
	        resourcePO.setCreateBy(userId);
	        resourcePO.setUpdateBy(userId);
	        resourcePOList.add(resourcePO);
	    }
	    return resourcePOList;
	}*/

    /**
     * 获取所有可用资源数据
     *
     * @return
     */
    public List<ResourcePO> getAllResource() {
        Map<String, Object> map = new HashMap<>(2);
        map.put("delFlag", EDeleteFlag.NORMAL.getKey());
        return resourceDAO.listBy(map);
    }

    /**
     * 解析节点深度
     *
     * @param list
     * @param pid
     * @param deep
     * @param reList
     * @return
     */
    private List<ResourcePO> generateResourceDeep(List<ResourcePO> list, Integer pid, Integer deep, List<ResourcePO> reList) {
        for (ResourcePO row : list) {
            if (row.getPid().equals(pid)) {
                //临时借用del_flag字段作为节点深度字段存储数据
                row.setDelFlag(String.valueOf(deep));
                reList.add(row);
                generateResourceDeep(list, row.getId().intValue(), deep + 1, reList);
            }
        }
        return reList;
    }

    /**
     * 获取下载数据文件
     * @return
     */
	/*public HSSFWorkbook exportResource(){
	    List<ResourcePO> list = getAllResource();
	    List<ResourcePO> reList = new ArrayList<>();
	    generateResourceDeep(list,0,0,reList);
	    return generateExportFile(reList);
	}*/

    /**
     * 生成下载数据文件
     * @param list
     * @return
     */
	/*private HSSFWorkbook generateExportFile(List<ResourcePO> list){
	    HSSFWorkbook wb = new HSSFWorkbook();
	    HSSFSheet sheet = wb.createSheet("一体化管控平台资源树");
	    HSSFRow row = sheet.createRow(0);
	    HSSFCell cell = null;
	    //最大深度
	    int maxDeep = 5;
	    String[] title = {"菜单名称","资源地址","平台标识符","菜单图标","资源标识符","服务类型（0内部，1外部）","资源排序"};
	    for(int i=0;i<title.length;i++){
	        cell = row.createCell(i == 0 ? 0 : i + maxDeep - 1 );
	        cell.setCellValue(title[i]);
	    }
	    int rowIndex = 0;
	    for(ResourcePO item : list){
	        row = sheet.createRow(rowIndex + 1);
	        int colIndex = Integer.valueOf(item.getDelFlag());
	        row.createCell(colIndex).setCellValue(item.getName());
	        //将列索引指向到最大深度并依次递增
	        colIndex = maxDeep;
	        row.createCell(colIndex++).setCellValue(item.getUrl());
	        row.createCell(colIndex++).setCellValue(item.getPrefix());
	        row.createCell(colIndex++).setCellValue(item.getMenuIcon());
	        row.createCell(colIndex++).setCellValue(item.getNameEn());
	        row.createCell(colIndex++).setCellValue(item.getServerType() == null ? 0 : item.getServerType());
	        row.createCell(colIndex++).setCellValue(item.getSort());
	        rowIndex ++;
	    }
	    return wb;
	}*/

    /**
     * 获取运维菜单
     *
     * @return
     * @throws BizException
     */
    public List<String> getOperationsMenu(Integer userId, QueryResourceTreeDTO queryResourceTreeDTO, String tid) throws BizException {
        try {
            //如果首次登录需要强制修改密码则返回空菜单
            UserPO userPO = userService.getUserById(userId);
            if ((StringUtils.isBlank(userPO.getLastLoginIp()) || userPO.getLastLoginTime() == null)) {
                if (userPO.getLastChangePwd() == null) {
                    Object obj = settingService.getSettingByKey(EUserSecurity.S_FIRST_LOGIN_CHANGE_PWD.getKey(), tid);
                    if (Optional.ofNullable(obj).map(v -> Integer.valueOf(obj.toString())).orElse(0) > 0) {
                        return new ArrayList<>();
                    }
                }
            }

            Map<String, Object> queryMap = new HashMap<>(2);
            queryMap.put("delFlag", EDeleteFlag.NORMAL.getKey());
            queryMap.put("prefix", queryResourceTreeDTO.getPrefix());
            //运维人员不区分权限与超级管理员拥有一样的权限 !_!
//            UserPO userPO = userService.getUserById(userId);
//            if (userId != null) {
//                if(userPO == null){
//                    throw new BizException("用户信息异常！");
//                }else if(!userPO.getName().equals(SysConstant.SYSTEM_USER_NAME) &&
//                        !userPO.getName().equals(SysConstant.ADMIN_USER_NAME)){
//                    queryMap.put("userId", userId);
//                }
//            }
            List<ResourcePO> list = resourceDAO.listByUser(queryMap);
            List<String> reList = new ArrayList<>();
            for (ResourcePO res : list) {
                reList.add(res.getUrl());
            }
            return reList;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new BizException("获取数据失败！");
        }
    }

    /**
     * 获取指定ID集的
     *
     * @param ids
     * @return
     */
    public List<QueryUserResourceVO> listByIds(String ids) throws BizException {
        if (StringUtils.isEmpty(ids)) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        String[] strIds = ids.split(",");
        List<Long> lIds = new ArrayList<>();
        try {
            for (String id : strIds) {
                lIds.add(Long.valueOf(id));
            }
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
        map.put("ids", lIds);
        return resourceDAO.queryResourceByIds(map);
    }

    /**
     * 生成资源的redis存储key
     *
     * @param resourceId
     * @return
     */
    private String getResourceRedisKey(Long resourceId, String tid) {
        tid = formatTid(tid);
        return "";
    }

    private String formatTid(String tid) {
        if (StringUtils.isEmpty(tid)) {
            tid = "";
        } else {
            if (tid.indexOf(",") > 0) {
                String[] tids = tid.split(",");
                for (String id : tids) {
                    if (StringUtils.isNotEmpty(id)) {
                        tid = id;
                        break;
                    }
                }
            }
            tid = tid + ":";
        }
        return tid;
    }

    /**
     * 缓存所有资源信息
     */
    public void storageRedisResource(String tid) {
        List<ResourcePO> list = resourceDAO.listBy(new HashMap<>());
        if (list != null && list.size() > 0) {
            list.forEach(item -> {
                this.addRedisResource(item, tid);
            });
        }
    }

    /**
     * 缓存资源
     *
     * @param resourcePO
     */
    public void addRedisResource(ResourcePO resourcePO, String tid) {
        String key = this.getResourceRedisKey(resourcePO.getId(), tid);
        Map map = redisService.getMap(key);
        if (map == null) {
            map = new HashMap(4);
        }
        map.put("id", String.valueOf(resourcePO.getId()));
        map.put("name", resourcePO.getName());
        map.put("pid", String.valueOf(resourcePO.getPid()));
        redisService.setMap(key, map, null);
    }

    /**
     * 获取指定平台的所有可用资源数据
     *
     * @param prefix
     * @return
     */
    public List<QueryUserResourceVO> queryByPrefix(String prefix) {
        return resourceDAO.queryByPrefix(prefix);
    }

    /**
     * 获取拥有指定资源的用户集
     *
     * @param id
     * @return
     */
    public List<Long> queryUserIdsByResourceId(Long id) {
        return resourceDAO.queryUserIdsByResourceId(id);
    }

    /**
     * 获取指定资源的所有上级节点ID
     *
     * @param list
     * @return
     */
    public List<Integer> getResourceIdsAndParentIds(List<Integer> list) {
        List<Integer> reList = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            return reList;
        }
        List<ResourcePO> resourcePOList = getAllResource();
        for (Integer id : list) {
            this.getResourceParentIds(resourcePOList, id, reList);
        }
        return reList.stream().distinct().collect(Collectors.toList());
    }

    private void getResourceParentIds(List<ResourcePO> resourcePOList, Integer id, List<Integer> reList) {
        for (ResourcePO po : resourcePOList) {
            if (po.getId().equals(Long.valueOf(id))) {
                reList.add(po.getId().intValue());
                if (po.getPid() > 0) {
                    getResourceParentIds(resourcePOList, po.getPid(), reList);
                }
            }
        }
    }
}
